using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.InputSystem;
namespace RuntimeCommand { 

public class CommandController : MonoBehaviour
{
    public static CommandController current;

    [HideInInspector]
    public bool showHistory = false;

    [HideInInspector]
    public List<Tuple<string, Type, string>> commands = new List<Tuple<string, Type, string>>();
    public Queue<string> historyContent;
    public HistoryStack historyQueue;

    public int historyHeight=100;
    public int cacheSize = 20;
    public int historySize=20;

    bool showConsole = false;
    string input;
    Vector2 scroll;
    public void Awake()
    {
        current= this;
        InitCommand();
        historyQueue = new HistoryStack(cacheSize);
        historyContent = new Queue<string>(historySize);
    }
    private void Update()
    {
        if (Keyboard.current.tabKey.wasReleasedThisFrame)
        {
            if (!lockKey)
            {
                OnToogleDebug();
            }
            lockKey = false;
        }

        if (Keyboard.current.enterKey.wasReleasedThisFrame)
        {
            OnReturn();
        }


    }

    private bool lockKey = false;
    private void OnGUI()
    {

        if (!showConsole) return;

        float y = Screen.height- historyHeight-30;
        if (showHistory)
        {
            GUI.Box(new Rect(0, y+5, Screen.width, historyHeight), "");
            Rect viewport = new Rect(0,y, Screen.width - 30, 20 * historyContent.Count);
            scroll = GUI.BeginScrollView(new Rect(0,y+10, Screen.width, historyHeight-10), scroll, viewport);

            int i = 0;
            foreach (var item in historyContent)
            {
                Rect labelRect = new Rect(5, y + 20 * i, viewport.width - 100, 20);
                GUI.Label(labelRect, item);
                i++;
            }
            GUI.EndScrollView();
            y -= historyHeight;
        }

        GUI.Box(new Rect(0, Screen.height-25, Screen.width, 30), "");
        GUI.backgroundColor = new Color(0, 0, 0, 0);
        GUI.SetNextControlName("InputField");
        input = GUI.TextField(new Rect(5f, Screen.height - 23f, Screen.width, 30), input);
        GUI.FocusControl("InputField");
        var e= Event.current;
        if (GUI.GetNameOfFocusedControl() == "InputField")
        {
            if (e.keyCode == KeyCode.Tab && e.type == EventType.KeyDown)
            {
                OnToogleDebug();
                lockKey = true;
            }
            if (e.keyCode == KeyCode.UpArrow&& e.type == EventType.KeyUp)
            {
                if (historyQueue.GetOld(out string data))
                {
                    input =data;
                }
            }
            if (e.keyCode == KeyCode.DownArrow && e.type == EventType.KeyUp)
            {
                if (historyQueue.GetNew(out string data))
                {
                    input = data;
                }
                else input = string.Empty;
            }
           
        }

    }
    /// <summary>
    ///Collect command
    /// </summary>
    private void InitCommand()
    {
        AppDomain.CurrentDomain.GetAssemblies()
                                .SelectMany(a => a.GetTypes().Where(t =>
                                {
                                    if (t.GetInterfaces().Contains(typeof(Command)))
                                    {
                                        var attr = t.GetCustomAttribute<CommandID>();
                                        if (attr != null)
                                        {
                                            commands.Add(new Tuple<string, Type, string>(attr.id, t, attr.information));

                                        }

                                    }
                                    return true;
                                })
                                ).ToList();

    }


    /// <summary>
    /// Find and execute the command
    /// </summary>
    /// <param name="param"></param>
    /// <param name="pa"></param>
    public void ExecuteCommand(string[] param, params object[] pa)
    {
        var type = commands.Find((i) => { return i.Item1 == param[0]; });
        if (type != null)
        {
            var cmd = (Command)Activator.CreateInstance(type.Item2, pa);
            object[] objs = new object[param.Length - 1];
            for (int i = 1; i < param.Length; i++)
            {
                objs[i - 1] = param[i];
            }
            Enqueue(input);
            cmd.Execute(objs);

        }
        else
        {
            Enqueue(input+ "  command not present");
            scroll.y += 20;
        }
    }

    public void AddHelp()
    {
        for (int i = 0; i < commands.Count; i++)
        {
            var temp = commands[i];
            string label = temp.Item1 + "     " + temp.Item3;
            Enqueue(label);
        }
    }

    private void Enqueue(string text)
    {
        if (historyContent.Count ==historySize)
        {
            historyContent.Dequeue();
        }
        historyContent.Enqueue(text);

    }


    public void OnToogleDebug()
    {
        showConsole = !showConsole;
       
    }

    public void OnReturn()
    {
        
        if (showConsole)
        {
            showHistory = true;
            HandleInput();
            input = "";
        }
    }

    private void HandleInput()
    {
        if (string.IsNullOrEmpty(input))
        {
            return;
        }
        Debug.Log("cmdInput:" + input);
        historyQueue.EnQueue(input);
        string[] param = input.Split(' ');
        ExecuteCommand(param);
        
    }
}
}